<?php
/**
 * polwebgis
 * 
 * This file is part of polwebgis.
 *
 * @copyright   Copyright (c) 2012 Dominik Janisch (http://www.joanneum.at)
 * @lastmodified    $LastChangedDate: 2012-04-03 09:23:39 +0000 (Tue, 03 Apr 2012) $
 * @revision    $Revision: 174 $ 
 */

require_once(PATH_TO_APP . "/components/gismap/Map.php");
require_once(PATH_TO_APP . "/components/gismap/ColorGradient.php");
require_once(PATH_TO_APP . "/components/gismap/MapClassProperties.php");
require_once(PATH_TO_APP . "/components/gismap/Layers.php");
require_once(PATH_TO_APP . "/components/excel/ExcelExport.php");
require_once(PATH_TO_APP . "/components/formathandler/FormatHandler.php");
require_once(PATH_TO_APP . "/components/analysis/Analysis.php");
require_once(PATH_TO_APP . "/components/analysis/tabledatagateway/AnalysisGateway.php");
require_once(PATH_TO_APP . "/components/classification/Classification.php");
require_once(PATH_TO_APP . "/components/printcomposer/PrintComposer.php");
require_once(PATH_TO_APP . "/components/gismap/tabledatagateway/LayersGateway.php");

DEFINE("PROPERTY_COLUMN_PREFIX", "column_");

class GisMap extends Map implements MapInterface
{  
    private $map_class_properties_;
    private $layer_;
    private $map_id_;
    private $arr_columns_ = array();  
    private $arr_table_columns_ = array(); 
    private $where_condition_ = '';
    private $map_list_;
    private $base_layer_;
    
    const POSTGRES_DATATYPE_INT = 'integer';
    
    const CASES_DATATYPE_INT = 1;
    
    const CASES_DATATYPE_NOT_INT = 0;
    
    const CLASSIFICATION_QUANTILE = 'quantile';
    
    const CLASSIFICATION_EQUAL = 'equal';
    
    const CLASSIFICATION_USER = 'user';
    
    const CLASSIFICATION_STDDEV = 'stddev';
    
    const CLASSIFICATION_JENKS = 'jenks';
    
    const CLASSIFICATION_PERCENTILE = 'percentile';
    
    const CLASSIFICATION_BOXMAP10 = 'boxmap10';
    
    const CLASSIFICATION_BOXMAP15 = 'boxmap15';
    
    const CLASSIFICATION_BOXMAP20 = 'boxmap20';
    
    const CLASSIFICATION_BOXMAP30 = 'boxmap30';
    
    const CLASSIFICATION_BOXMAP40 = 'boxmap40';
    
    const CLASS_WIDTH = 10;
    
    const ANALYSIS_HISTOGRAM = 'histogram';
    
    const ANALYSIS_STATS = 'stats';
    
    const ANALYSIS_BOXPLOT = 'boxplot';
    
    const ANALYSIS_DIAGRAM = 'diagram';
    
    const ANALYSIS_DATATABLE = 'datatable';
    
    const ANALYSIS_SPATIAL_ASSOCIATION = 'moran';
    
    const CHART_DATA_SORT_REGION = 1;
    
    const CHART_DATA_SORT_CASES = 2;

    public function __construct($is_wms, $table_gateway, $singleton_registry, $map_id, &$map_list, $base_layer)
    {
        $this->table_gateway_ = $table_gateway;
        $this->is_wms_ = $is_wms;
        $this->map_id_ = $map_id;
        $this->base_layer_ = $base_layer;
        $this->output_handler_ = $singleton_registry->getSingleton('OutputHandler');
        $this->request_ = $singleton_registry->getSingleton('Request');
        $this->session_ = $singleton_registry->getSingleton('Session');
        $this->language_handler_ = $singleton_registry->getSingleton('LanguageHandler');
        $this->singleton_registry_ = $singleton_registry;
        $this->map_list_ = $map_list;
        if($this->map_list_->get('class_width') === false)
            $this->map_list_->set('class_width', constant("self::CLASS_WIDTH"));
        if($this->map_list_->get('class_method') === false)
            $this->map_list_->set('class_method', constant("self::CLASSIFICATION_EQUAL"));
        if($this->map_list_->get('cases_datatype') === false)
            $this->map_list_->set('cases_datatype', constant("self::CASES_DATATYPE_INT"));
        if($this->map_list_->get('legend_position') === false)
            $this->map_list_->set('legend_position', $this->getMSPosition('MS_LL'));
        if($this->map_list_->get('scalebar_position') === false)
            $this->map_list_->set('scalebar_position', $this->getMSPosition('MS_LR'));
        $this->session_name_ = $this->map_list_->get('session_name');
    }
        
    public function __destruct(){}
    
    public function init()
    {
        if(!$this->getDataDetails())
            throw new Exception('Illegal Map-ID');
        $this->checkForSetColors();
        //$this->checkSession();
        $this->setTableColumns();
        if($this->isLayerModification())
            return true;
        $this->getFeatureCharacteristic();
        $this->checkInput();
        $this->getCasesDimensions();
        if($this->request_->issetParameter('get', 'mode') && $this->request_->getParameter('get', 'mode') == "ajax")
        {
            $this->calculateLimits();
            return true;
        }
        $this->map_ = ms_newMapObj("bezirke.map");
        $this->map_->set('units', $this->getUnit($this->table_details_->e_unit));
        if($this->isAnalysis())
            return true;
        $this->map_->setProjection('init=epsg:' . $this->table_details_->e_projection);
        $arr_extent = explode(';', $this->table_details_->e_bbox);
        if(count($arr_extent) != 4)
            throw new Exception('Illegal Bounding Box');
        $this->map_->setExtent($arr_extent[0], $arr_extent[1], $arr_extent[2], $arr_extent[3]);
        $this->output_handler_->assignValuesToTemplate(array("map_projection" => $this->table_details_->e_projection, "arr_map_extent" => $arr_extent, "map_max_resolution" => $this->table_details_->e_resolution, "map_description" => $this->table_details_->dt_beschreibung, "topic_id" => $this->table_details_->t_id, "topic_name" => str_replace(':project', $this->table_details_->t_name, $this->language_handler_->getLanguageString('switch_to_project'))));
        $this->prepareLayer();
        new Layers(&$this->map_, &$this->map_list_, $this->request_, true, $this->table_details_->e_id, $this->table_details_->e_projection);
        if(!$this->is_wms_)
            $this->addScalebarAndLegend();
        if($this->is_wms_)
            $this->showWms();
        else
        {
            $printcomposer = new PrintComposer(&$this->map_);
            $this->output_handler_->assignValuesToTemplate(array("arr_paper_sizes" => $printcomposer->getPaperSizes($this->language_handler_->getLanguageString('format_string_paper_sizes'))));
            $show_map = true;
            if($this->request_->issetParameter('post', 'export') && $this->exportMap($printcomposer))
                $show_map = false;
            if($this->request_->issetParameter('post', 'excelexport'))
            {
                $arr_tmp = $this->getChartData(true);
                $excel_export = new ExcelExport($arr_tmp, $this->table_details_, $this->singleton_registry_);
                if($excel_export->export())
                    $show_map = false;
                else
                    $this->output_handler_->assignValuesToTemplate(array("arr_error_messages" => array($this->language_handler_->getLanguageString('error_excel_export'))));
            }
            if($show_map)
                $this->showMap();
        }
        return true;
    }
    
    private function prepareLayer($show_legend = false)
    {
        if($this->session_->get('js_activated') === false || $this->is_wms_ || $this->request_->issetParameter('post', 'export') || $this->request_->issetParameter('get', 'debug'))
        {
            $this->layer_ = ms_newLayerObj($this->map_);
            $this->layer_->set('type', MS_LAYER_POLYGON);
            $this->layer_->setConnectionType(MS_POSTGIS);
            $this->layer_->set('connection', MAPSERVER_CONNECTION_STRING);
            $this->layer_->set('status', MS_ON);
            $this->layer_->set('name', 'base_layer');
            if(isset($this->table_details_->e_id))
            {
                $this->base_layer_->prepareMapserverLayer($this->layer_, $this->table_details_->e_id, $this->table_details_->e_projection, $this->table_details_->dt_tabelle, $this->table_gateway_->getWhereCondition());
            }
            //$this->layer_->set('classitem', 'gid');
            if(!$show_legend)
            {
                if($this->map_list_->get('class_method') != constant("self::CLASSIFICATION_QUANTILE") && $this->map_list_->get('class_method') != constant("self::CLASSIFICATION_PERCENTILE"))
                    $this->calculateEqualClasses(new FormatHandler());
                else
                    $this->calculateQuantileClasses(new FormatHandler());
            }
            $class_obj = ms_newClassObj($this->layer_);
            $class_obj->set('name', $this->language_handler_->getLanguageString('legend_data_without_region'));
            $style = ms_newStyleObj($class_obj);
            $style->color->setRGB(hexdec(substr($this->map_list_->get('base_color'), 0, 2)), hexdec(substr($this->map_list_->get('base_color'), 2, 2)), hexdec(substr($this->map_list_->get('base_color'), 4, 2)));
            $style->outlinecolor->setRGB(hexdec(substr($this->map_list_->get('border_color'), 0, 2)), hexdec(substr($this->map_list_->get('border_color'), 2, 2)), hexdec(substr($this->map_list_->get('border_color'), 4, 2)));
        }
        else
        {
            if($this->map_list_->get('class_method') != constant("self::CLASSIFICATION_QUANTILE") && $this->map_list_->get('class_method') != constant("self::CLASSIFICATION_PERCENTILE"))
                $this->calculateLimits();
        }
    }
    
    private function calculateLimits()
    {
        $classification = new Classification($this->singleton_registry_, &$this->map_class_properties_);
        $arr_limits = array();
        if($this->map_list_->get('class_method') == constant("self::CLASSIFICATION_USER") && $this->request_->issetParameter('post', 'arr_classlimits') && !$this->request_->issetParameter('get', 'reset'))
        {
            $arr_tmp = $this->request_->getParameter('post', 'arr_classlimits');
            sort($arr_tmp, SORT_NUMERIC);
            foreach($arr_tmp as $key => $value)
            {
                $arr_limits[$key]['condition'] = "cases >= :from and cases < :to";
                $arr_limits[$key]['label'] = ":from < :to";
                $arr_limits[$key]['value'] = $value;
            }
            $this->session_->set($this->session_name_.'.classlimits', $arr_limits);
        }
        if($this->map_list_->get('class_method') == constant("self::CLASSIFICATION_USER") && $this->session_->get($this->session_name_.'.classlimits') !== false && !$this->request_->issetParameter('get', 'reset'))
            $arr_limits = $this->session_->get($this->session_name_.'.classlimits');
        else
        {
            if($this->request_->issetParameter('get', 'reset'))
            {
               $arr_limits = $classification->getEqualBreaks($this->map_list_->get('class_width')); 
            }
            else
            {
                switch($this->map_list_->get('class_method'))
                {
                    case constant("self::CLASSIFICATION_STDDEV"):
                        $arr_limits = $classification->getStddevBreaks(&$this->map_list_);
                        break;
                    case constant("self::CLASSIFICATION_JENKS"):
                        $arr_limits = $classification->getJenksBreaks($this->table_gateway_->getMapCases($this->table_details_->dt_tabelle), $this->map_list_->get('class_width'));
                        break;
                    case constant("self::CLASSIFICATION_BOXMAP10"):
                        $arr_limits = $classification->getBoxMapBreaks($this->table_gateway_->getMapCases($this->table_details_->dt_tabelle), &$this->map_list_, 1, $this->map_list_->get('hide_values'));
                        break;
                    case constant("self::CLASSIFICATION_BOXMAP15"):
                        $arr_limits = $classification->getBoxMapBreaks($this->table_gateway_->getMapCases($this->table_details_->dt_tabelle), &$this->map_list_, 1.5, $this->map_list_->get('hide_values'));
                        break;
                    case constant("self::CLASSIFICATION_BOXMAP20"):
                        $arr_limits = $classification->getBoxMapBreaks($this->table_gateway_->getMapCases($this->table_details_->dt_tabelle), &$this->map_list_, 2, $this->map_list_->get('hide_values'));
                        break;
                    case constant("self::CLASSIFICATION_BOXMAP30"):
                        $arr_limits = $classification->getBoxMapBreaks($this->table_gateway_->getMapCases($this->table_details_->dt_tabelle), &$this->map_list_, 3, $this->map_list_->get('hide_values'));
                        break;
                    case constant("self::CLASSIFICATION_BOXMAP40"):
                        $arr_limits = $classification->getBoxMapBreaks($this->table_gateway_->getMapCases($this->table_details_->dt_tabelle), &$this->map_list_, 4, $this->map_list_->get('hide_values'));
                        break;
                    default:
                        $arr_limits = $classification->getEqualBreaks($this->map_list_->get('class_width'));
                }
            }
        }
        $arr_limits = $this->setLimitDatatype($arr_limits);
        if($this->request_->issetParameter('get', 'mode') && $this->request_->getParameter('get', 'mode') == "ajax")
        {
            $arr_json = array();
            foreach($arr_limits as $value)
                $arr_json[] = $value['value'];
            $this->output_handler_->showJSON($arr_json);
        }
        else
            $this->output_handler_->assignValuesToTemplate(array("arr_class_limits" => $arr_limits));
        return $arr_limits;
    }
    
    private function setLimitDatatype($arr_tmp)
    {
        $arr_limits = $arr_tmp;
        foreach($arr_tmp as $key => $value)
        {
            if($this->map_list_->get('cases_datatype') == constant("self::CASES_DATATYPE_INT"))
                $arr_limits[$key]['value'] = (int) $value['value'];
            else
                $arr_limits[$key]['value'] = (float)str_replace(",", ".", $value['value']);
        }
        return $arr_limits;
    }
    
    private function calculateEqualClasses($format_handler)
    { 
        $arr_legend = array();
        $arr_histogram = array();
        $arr_limits = $this->calculateLimits();
        $color_gradient = new ColorGradient($this->map_list_->get('start_color'), $this->map_list_->get('end_color'), $this->map_list_->get('class_width'));
        $is_int = $this->map_list_->get('cases_datatype') == constant("self::CASES_DATATYPE_INT") ? true : false;
        for($cnt=0; $cnt < count($arr_limits)-1; ++$cnt)
        {  
            $expression_condition = str_replace("cases", "[cases]", $arr_limits[$cnt]['condition']);
            $expression = "(".str_replace(":from", $arr_limits[$cnt]['value'], str_replace(":to", $arr_limits[$cnt+1]['value'], $expression_condition)).")";
            //TODO: correct amount of regions
            $row_cnt = 0;
            $class_name = str_replace(":from", $format_handler->getOutput($arr_limits[$cnt]['value'], $is_int), str_replace(":to", $format_handler->getOutput($arr_limits[$cnt+1]['value'], $is_int), $arr_limits[$cnt]['label'])) .' ('.$row_cnt.')'; 
            $arr_legend[] = $class_name; 
            $this->setLayerProperties($class_name, $cnt, $expression, $color_gradient);
            if($this->map_list_->get('histogram'))
                $arr_histogram[] = $this->calcHistogramEntry($class_name, $row_cnt, $arr_limits[$cnt+1]['value'], $arr_limits[$cnt]['value']);
        }
        $this->map_list_->set('arr_legend', $arr_legend);
        if($this->map_list_->get('histogram'))
            $this->map_list_->set('arr_histogram', $arr_histogram);
    }
    
    private function calculateQuantileLimits()
    {
        $classification = new Classification($this->singleton_registry_, &$this->map_class_properties_);
        $arr_limits = array();
        switch($this->map_list_->get('class_method'))
        {
            case constant("self::CLASSIFICATION_PERCENTILE"):
                $arr_limits = $classification->getPercentileBreaks(&$this->map_list_);
                break;
            default:
                $arr_limits = $classification->getQuantileBreaks($this->map_list_->get('class_width'));
        }
        return $arr_limits;
    }
    
    private function calculateQuantileClasses($format_handler)
    {
        $arr_legend = array();
        $arr_histogram = array();
        $tmp_end = 0;
        $offset = 0;
        $arr_gids = array();
        $is_int = $this->map_list_->get('cases_datatype') == constant("self::CASES_DATATYPE_INT") ? true : false;
        $arr_limits = $this->calculateQuantileLimits();
        $color_gradient = new ColorGradient($this->map_list_->get('start_color'), $this->map_list_->get('end_color'), $this->map_list_->get('class_width')); 
        for($cnt=0; $cnt < $this->map_list_->get('class_width'); ++$cnt)
        {
            $legend_min = $this->map_class_properties_->getMax();
            $legend_max = $this->map_class_properties_->getMin();
            $row_cnt = $this->table_gateway_->calculateQuantileClasses($this->table_details_->dt_tabelle, $arr_limits[$cnt], &$arr_gids, &$legend_max, &$legend_min);
            if($legend_max < $legend_min)
                $legend_max = $legend_min;
            $legend_max = round($legend_max,2);
            $expression = "([cases] >= ".$legend_min." and [cases]<= ".$legend_max.")";
            if($cnt == 0)
                $tmp_end = round($legend_min, 2);
            $class_name = $format_handler->getOutput($tmp_end, $is_int). ' <= ' . $format_handler->getOutput($legend_max, $is_int) .' ('.$row_cnt.')'; 
            $this->setLayerProperties($class_name, $cnt, $expression, $color_gradient);
            if($this->map_list_->get('histogram'))
                $arr_histogram[] = $this->calcHistogramEntry($class_name, $row_cnt, $legend_max, $tmp_end);
            $tmp_end = $legend_max;
            $arr_legend[] = $class_name; 
        }
        $this->map_list_->set('arr_legend', $arr_legend);
        if($this->map_list_->get('histogram'))
            $this->map_list_->set('arr_histogram', $arr_histogram);
    }
    
    private function calcHistogramEntry($class_name, $cases, $max, $min)
    {
        if(($this->map_list_->get('class_method') == constant("self::CLASSIFICATION_QUANTILE") || $this->map_list_->get('class_method') == constant("self::CLASSIFICATION_USER") || $this->map_list_->get('class_method') == constant("self::CLASSIFICATION_EQUAL")) && $max > $min)
        {
            $arr_histogram_tmp = array();
            $arr_histogram_tmp['class_name'] = $class_name;
            $arr_histogram_tmp['cases'] = $cases;
            $arr_histogram_tmp['relative_frequency'] = $cases / $this->map_class_properties_->getCases();
            $arr_histogram_tmp['width'] = $max - $min;
            $arr_histogram_tmp['width_percentaged'] = $arr_histogram_tmp['width'] / ($this->map_class_properties_->getMax()-$this->map_class_properties_->getMin());
            $arr_histogram_tmp['height'] = $arr_histogram_tmp['cases']/$arr_histogram_tmp['width_percentaged'];
            return $arr_histogram_tmp;
        }
        return array();
    }
    
    private function setLayerProperties($class_name, $cnt, $expression, $color_gradient)
    {
        $class_obj = ms_newClassObj($this->layer_);
        $class_obj->set('name',  $class_name);
        if($expression == '()')
            $expression = "'[gid]' == 'auto".$cnt."'";
        $class_obj->setExpression($expression);
        $style = ms_newStyleObj($class_obj);
        $arr_colors = $color_gradient->getColor($cnt);
        $style->color->setRGB($arr_colors[0], $arr_colors[1], $arr_colors[2]);
        $style->outlinecolor->setRGB(hexdec(substr($this->map_list_->get('border_color'), 0, 2)), hexdec(substr($this->map_list_->get('border_color'), 2, 2)), hexdec(substr($this->map_list_->get('border_color'), 4, 2)));
        $class_obj->label->outlinecolor->setRGB(255,255,255);
        $class_obj->label->set('minfeaturesize',5);
        $class_obj->label->set('font', 'arial');
        $class_obj->label->set('type', 'truetype');
        $class_obj->label->set('size', $this->map_list_->get('map_fontsize_label'));
    }
    
    private function exportMap(PrintComposer $printcomposer)
    {
        $printcomposer->addLegendAndScalebarPosition($this->map_list_->get('legend_position'), $this->map_list_->get('scalebar_position'));
        $printcomposer->createExport($this->request_->getParameter('post', 'paper_size'), $this->table_details_, $this->request_);
        $arr_extent = $this->session_->get($this->session_name_.'.bbox');
        if($arr_extent !== false && $this->request_->issetParameter('post', 'export_tile'))
            $this->map_->setExtent($arr_extent[0], $arr_extent[1], $arr_extent[2], $arr_extent[3]);
        $filename = $this->table_details_->dt_titel . "_" . $this->table_details_->g_name . "_" . $this->table_details_->e_name;
        $this->output_handler_->addHeader("Content-Type", "image/png");
        $this->output_handler_->addHeader('Content-Disposition', 'attachment; filename="'.preg_replace("/[^0-9A-Za-z_ÄäÖöÜü]/", '', $filename).'.png"');
        $this->output_handler_->showHeaders();
        $this->createMap()->saveImage('');
        return true;
    }
    
    private function showMap()
    {
        $this->output_handler_->addHeader("Content-Type", "text/html; charset=utf-8");
        ms_resetErrorList();
        $imageurl = -1;
        if($this->session_->get('js_activated') === false)
            $imageurl = $this->createMap()->saveWebImage('MS_PNG', 1,1, 0);
        setcookie("mapcolors", $this->map_list_->get('start_color').'|'.$this->map_list_->get('end_color'), time()+3600*24*365);
        $this->map_list_->set('legend_position_string', $this->getPostionString($this->map_list_->get('legend_position')));
        $this->map_list_->set('scalebar_position_string', $this->getPostionString($this->map_list_->get('scalebar_position')));
        $this->map_list_->set('arr_table_columns', $this->arr_table_columns_);
        if($this->session_->get($this->session_name_.'.bbox') !== false)
        {
            $this->output_handler_->assignValuesToTemplate(array("arr_zoom_extent" => $this->session_->get($this->session_name_.'.bbox')));
        }    
        $arr_params = $this->map_list_->getArray();
        $arr_layers = $this->table_gateway_->getAvailableLayers($this->table_details_->e_projection, $this->table_details_->e_id); 
//        $this->session_->set($this->session_name_.'.arr_params', $arr_params);
        $this->output_handler_->assignValuesToTemplate(array("imageurl" => $imageurl, "wms_url" => BASE_URI."index.php?input=wms&m_id=".$this->map_id_, "legend_url" => BASE_URI."index.php?m_id=".$this->map_id_, "arr_params" => $arr_params, "map_title" => $this->table_details_->dt_titel, "page_title" => $this->table_details_->dt_titel, "arr_layers" => $arr_layers));
        $this->output_handler_->showTemplate("map");
    }
    
    private function showWms()
    {
        $this->session_->set('js_activated', true);
        $this->map_->setSize((int)$this->request_->getParameter('get', 'WIDTH'), (int)$this->request_->getParameter('get', 'HEIGHT'));
        $this->session_->set($this->session_name_.'.width', (int)$this->request_->getParameter('get', 'WIDTH'));
        $this->session_->set($this->session_name_.'.height', (int)$this->request_->getParameter('get', 'HEIGHT'));
        $arr_extent = explode(',', $this->request_->getParameter('get', 'BBOX'));
        $this->session_->set($this->session_name_.'.bbox', $arr_extent);
        $arr_params = $this->map_list_->getArray();
        $this->session_->set($this->session_name_.'.arr_params', $arr_params);
        $this->map_->setExtent($arr_extent[0], $arr_extent[1], $arr_extent[2], $arr_extent[3]);
        $this->output_handler_->addHeader("Content-Type", "image/png");
        $this->output_handler_->addHeader("Last-Modified:", date("D, d M Y H:i:s T", time() - 30));
        $this->output_handler_->addHeader("Cache-Control:", "no-store, no-cache, must-revalidate");
        //header("Cache-Control: post-check=0, pre-check=0", false);
        $this->output_handler_->showHeaders();
        $this->createMap()->saveImage('');
    }
    
    private function getChartData($export_data=false, $sort_order=1, $datastructure=1)
    {
        $arr_query_columns = array();
        if(count($this->arr_columns_) > 0)
        {
            foreach($this->arr_columns_ as $key => $value)
            {
                if(!isset($this->arr_table_columns_[$key]) || $this->arr_table_columns_[$key] == '-1')   
                    $arr_query_columns[pg_escape_string($key)] = pg_escape_string($key);
            }
        }
        $cnt = 0; 
        $arr_values = array();
        $arr_meta_data = array();
        $cases_type_is_int = $this->map_list_->get('cases_datatype') == constant("self::CASES_DATATYPE_INT") ? true : false;
        $this->table_gateway_->getChartData($arr_query_columns, $this->table_details_, &$arr_values, $cases_type_is_int, $this->map_class_properties_->getMax(), new FormatHandler(), $sort_order, $datastructure);
        end($arr_query_columns);
        $running = true;
        $elements = 0;
        while($running)
        {
            $key = key($arr_query_columns);
            $arr_meta_data[$key]['length'] = $elements;
            $arr_meta_data[$key]['values'] = $this->arr_columns_[$key];
            $elements = count($this->arr_columns_[$key]);
            if(prev($arr_query_columns) === false)
                $running = false;
        }
        $arr_meta_data = array_reverse($arr_meta_data, true);
        reset($arr_meta_data); // make sure array pointer is at first element 
        $arr_meta_data[key($arr_meta_data)]['dimension'] = count($arr_meta_data);
        $iteration = 1;
        foreach($arr_meta_data as $key => $arr_value)
        {
            $arr_meta_data[$key]['iteration'] = $iteration;
            $iteration *= $arr_value['length'];  
        }
        if($export_data)
        {
            $arr_return['values'] = $arr_values;
            $arr_return['meta'] = $arr_meta_data;
            return $arr_return; 
        }
        $this->output_handler_->assignValuesToTemplate(array("arr_values" => $arr_values, "arr_meta_data" => $arr_meta_data));
    }
    
    private function addScalebarAndLegend()
    {
        $this->map_->legend->set('status', MS_EMBED);
        $this->map_->legend->set('position', $this->map_list_->get('legend_position'));
        $this->map_->legend->label->set('size', '9');
        $this->map_->legend->set('keysizex', '15');
        $this->map_->legend->set('keysizey', '10');
        $this->map_->scalebar->set('status', MS_EMBED);
        $this->map_->scalebar->set('position', $this->map_list_->get('scalebar_position'));
        $this->map_->scalebar->set('units',  MS_KILOMETERS);
    }
    
    private function getDataDetails()
    {    
        $this->table_details_ = $this->table_gateway_->getDataDetails($this->map_id_);
        if($this->table_details_ === false)
            return false;
        return true;
    }
    
    private function getCasesDimensions()
    {
        $cnt = 0;
        if(count($this->arr_table_columns_) > 0)
            $this->table_gateway_->createWhereCondition($this->arr_table_columns_);
        $arr_dim = $this->table_gateway_->getCasesDimensions($this->table_details_->dt_tabelle);
        if($arr_dim === false)
            throw new Exception("Database Error");
        $this->map_class_properties_ = new MapClassProperties($arr_dim['min'], $arr_dim['max'], $arr_dim['cases'], $arr_dim['stddev'], $arr_dim['mean'], $this->map_list_->get('class_width'), $this->map_list_->get('cases_datatype'));
    }
    
    private function getFeatureCharacteristic()
    {
        $arr_tmp =explode('.', $this->table_details_->dt_tabelle);
        if(count($arr_tmp) == 2)
        {
            $this->arr_columns_ = $this->table_gateway_->getColumnNamesFromTablesWithSchema($arr_tmp, $this->table_details_->dt_tabelle);
            $data_type = $this->table_gateway_->getDataTypeFromTablesWithSchema($arr_tmp);
        }
        else
        {
            $this->arr_columns_ = $this->table_gateway_->getColumnNamesFromTablesWithoutSchema($this->table_details_->dt_tabelle);
            $data_type = $this->table_gateway_->getDataTypeFromTablesWithoutSchema($this->table_details_->dt_tabelle);
        }
        if($data_type === constant("self::POSTGRES_DATATYPE_INT"))
            $this->map_list_->set('cases_datatype', constant("self::CASES_DATATYPE_INT"));
        else
            $this->map_list_->set('cases_datatype', constant("self::CASES_DATATYPE_NOT_INT"));
        $this->output_handler_->assignValuesToTemplate(array("arr_columns" => $this->arr_columns_, "property_column_prefix" => PROPERTY_COLUMN_PREFIX));
    }
    
    private function checkInput()
    {
        $this->base_layer_->checkInput($this->request_);
        if($this->request_->issetParameter('post', 'classwidth') && (int)$this->request_->getParameter('post', 'classwidth')>1)
        {
            $class_width = (int)$this->request_->getParameter('post', 'classwidth') > constant("self::CLASS_WIDTH") ? constant("self::CLASS_WIDTH") : (int)$this->request_->getParameter('post', 'classwidth');
            $this->map_list_->set('class_width', $class_width);
        }
        if($this->request_->issetParameter('get', 'classwidth') && (int)$this->request_->getParameter('get', 'classwidth')>1)
        {
            $class_width = (int)$this->request_->getParameter('get', 'classwidth') > constant("self::CLASS_WIDTH") ? constant("self::CLASS_WIDTH") : (int)$this->request_->getParameter('get', 'classwidth');
            $this->map_list_->set('class_width', $class_width);
        }
        if($this->request_->issetParameter('post', 'classmethod'))
        { 
            switch($this->request_->getParameter('post', 'classmethod'))
            {
                case constant("self::CLASSIFICATION_QUANTILE"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_QUANTILE"));
                    break;
                case constant("self::CLASSIFICATION_USER"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_USER"));
                    break;
                case constant("self::CLASSIFICATION_STDDEV"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_STDDEV"));
                    break;
                case constant("self::CLASSIFICATION_JENKS"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_JENKS"));
                    break;
                case constant("self::CLASSIFICATION_PERCENTILE"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_PERCENTILE"));
                    break;
                case constant("self::CLASSIFICATION_BOXMAP10"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_BOXMAP10"));
                    break;
                case constant("self::CLASSIFICATION_BOXMAP15"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_BOXMAP15"));
                    break;
                case constant("self::CLASSIFICATION_BOXMAP20"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_BOXMAP20"));
                    break;
                case constant("self::CLASSIFICATION_BOXMAP30"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_BOXMAP30"));
                    break;
                case constant("self::CLASSIFICATION_BOXMAP40"):
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_BOXMAP40"));
                    break;
                default:
                    $this->map_list_->set('class_method', constant("self::CLASSIFICATION_EQUAL"));
            }
        }
        $this->setColor('base_color');
        $this->setColor('start_color');
        $this->setColor('end_color');
        $this->setColor('border_color');
        if($this->request_->issetParameter('post', 'classmethod'))
        {
            if($this->request_->issetParameter('post', 'hide_values'))
            {
                $this->map_list_->set('hide_values', true);
            }
            else
            {
                $this->map_list_->set('hide_values', false);
            }
        }
        if($this->request_->issetParameter('post', 'legend_position'))
            $this->map_list_->set('legend_position', $this->getMSPosition($this->request_->getParameter('post', 'legend_position')));
        if($this->request_->issetParameter('post', 'scalebar_position'))
             $this->map_list_->set('scalebar_position', $this->getMSPosition($this->request_->getParameter('post', 'scalebar_position')));
        if(count($this->arr_columns_) > 0)
        {
            foreach($this->arr_columns_ as $key => $value)
            {
                if($this->request_->issetParameter('post', PROPERTY_COLUMN_PREFIX . $key))
                    $this->arr_table_columns_[$key] = $this->request_->getParameter('post', PROPERTY_COLUMN_PREFIX . $key);
            }
        }
    }
    
    private function setTableColumns()
    {
        /*$arr_variables = $this->session_->get($this->session_name_.'.arr_params');
        if($arr_variables === false)
            return false;
        $this->map_list_->setArray($arr_variables);*/
        if(count($this->map_list_->get('arr_table_columns')) != 0 && $this->map_list_->get('arr_table_columns') !== false)
            $this->arr_table_columns_ = $this->map_list_->get('arr_table_columns'); 
        
    }
    
    private function checkForSetColors()
    {
        if($this->request_->issetParameter('cookie', 'mapcolors'))
        {
          $arr_colors = explode('|', $this->request_->getParameter('cookie', 'mapcolors'));
          if(count($arr_colors) == 2)
          {
            $tmp_start_color = preg_replace("/[^0-9A-Fa-f]/", '', $arr_colors[0]);
            $tmp_end_color = preg_replace("/[^0-9A-Fa-f]/", '', $arr_colors[1]);
            $this->map_list_->set('start_color' ,strlen($tmp_start_color)==6 ? $tmp_start_color : $this->map_list_->get('start_color'));
            $this->map_list_->set('end_color' ,strlen($tmp_end_color)==6 ? $tmp_end_color : $this->map_list_->get('end_color'));
          }
        }
    }
    
    private function setColor($param)
    {
        if($this->request_->issetParameter('post', $param))
        {
            $tmp_color = preg_replace("/[^0-9A-Fa-f]/", '', $this->request_->getParameter('post', $param));
            if(strlen($tmp_color) == 6)
                $this->map_list_->set($param, $tmp_color);
        }
    }
    
    private function getMSPosition($position)
    {
        switch($position){
            case "MS_UL":
                return MS_UL;
            case "MS_UR":
                return MS_UR;
            case "MS_UC":
                return MS_UC;
            case "MS_CC":
                return MS_CC;
            case "MS_LR":
                return MS_LR;
            case "MS_LC":
                return MS_LC;
            case "MS_CL":
                return MS_CL;
            case "MS_CR":
                return MS_CR;
            default:
                return MS_LL;
        }
    }
    
    private function getPostionString($position)
    {
        switch($position){
            case MS_UL:
                return "MS_UL";
            case MS_UR:
                return "MS_UR";
            case MS_UC:
                return "MS_UC";
            case MS_CC:
                return "MS_CC";
            case MS_LR:
                return "MS_LR";
            case MS_LC:
                return "MS_LC";
            case MS_CL:
                return "MS_CL";
            case MS_CR:
                return "MS_CR";
            default:
                return "MS_LL";
        }
    }
    
    private function getUnit($unit)
    {
        switch($unit)
        {
            case 'dd':
                return MS_DD;
            case 'feet':
                return MS_FEET;
            case 'inches':
                return MS_INCHES;
            case 'kilometers':
                return MS_KILOMETERS;
            case 'miles':
                return MS_MILES;
            case 'nauticalmiles':
                return MS_NAUTICALMILES;
            default:
                return MS_METERS;
        }
    }
    
    private function showLegend($map_id)
    {
        $this->map_id_ = (int)$map_id;
        $this->is_wms_ = true;
        //$this->checkSession();
        $this->setTableColumns();
        $this->map_->outputformat->set('transparent', MS_OFF);
        $this->prepareLayer(true);
        $arr_legend = $this->map_list_->get('arr_legend');
        $color_gradient = new ColorGradient($this->map_list_->get('start_color'), $this->map_list_->get('end_color'), $this->map_list_->get('class_width'));
        $arr_tmp = range('a', 'z');
        for($cnt=0; $cnt < $this->map_list_->get('class_width'); ++$cnt)
        {
            $expression = "'[gid]' == '".$arr_tmp[$cnt % 26]."'";
            $this->setLayerProperties($arr_legend[$cnt], $cnt, $expression, $color_gradient);
        }
    }
    
    public function wmsRequest($is_legend, $map_id)
    {
        $this->map_ = ms_newMapObj("bezirke.map");
        if($is_legend)
            $this->showLegend($map_id);
        $request = ms_newowsrequestobj();
        foreach ($_REQUEST as $key => $value) {
            $request->setParameter($key, $value);
        }
        ms_ioinstallstdouttobuffer();
        $this->map_->owsdispatch($request);
        $contentType = ms_iostripstdoutbuffercontenttype();
        if($contentType !== null)
        {
            header('Content-type: ' . $contentType);
            ms_iogetStdoutBufferBytes();
        }
        ms_ioresethandlers();
    }
    
    private function isAnalysis()
    {
        if($this->request_->issetParameter('get', 'analysis'))
        {
            $analysis = new Analysis($this->singleton_registry_, new AnalysisGateway());
            $this->output_handler_->assignValuesToTemplate(array("amode" => "normal", "map_title" => $this->table_details_->dt_titel, "m_id" => $this->map_id_));
            if($this->request_->issetParameter('get', 'amode'))
                $this->output_handler_->assignValuesToTemplate(array("amode" => "ajax"));
            $this->output_handler_->addHeader("Content-Type", "text/html; charset=utf-8");
            $is_int = $this->map_list_->get('cases_datatype') == constant("self::CASES_DATATYPE_INT") ? true : false;
            switch($this->request_->getParameter('get', 'analysis'))
            {
                case constant("self::ANALYSIS_HISTOGRAM"):
                    $this->map_list_->set('histogram', true);
                    $this->is_wms_ = true;
                    $this->prepareLayer();
                    $analysis->calculateHistogram(&$this->map_list_);
                    $this->output_handler_->assignValuesToTemplate(array("page_title" => $this->language_handler_->getLanguageString('analysis_histogram_title') . ": " . $this->table_details_->dt_titel));
                    break;
                case constant("self::ANALYSIS_BOXPLOT"):
                    $hinge = 1.5;
                    if($this->request_->issetParameter('post', 'hinge'))
                        $hinge = $this->request_->getParameter('post', 'hinge') == 3.0 ? 3.0 : $hinge;
                    $analysis->calculateBoxPlot($this->map_class_properties_, new Classification($this->singleton_registry_, &$this->map_class_properties_), $this->table_gateway_->getMapCasesAndRegion($this->table_details_->dt_tabelle, $this->table_details_->e_id), $hinge, $this->map_list_->get('class_width'), new FormatHandler(), $is_int);
                    $this->output_handler_->assignValuesToTemplate(array("map_id" => $this->map_id_, "hinge" => $hinge, "analysis_title" => $this->language_handler_->getLanguageString('analysis_boxplot'), "page_title" => $this->language_handler_->getLanguageString('analysis_boxplot_title') . ": ".$this->table_details_->dt_titel));
                    break;
                case constant("self::ANALYSIS_DIAGRAM"):
                    $sort_order = constant("self::CHART_DATA_SORT_REGION");
                    if($this->request_->issetParameter('post', 'chart_sort'))
                    {
                        $sort_order = $this->request_->getParameter('post', 'chart_sort') == constant("self::CHART_DATA_SORT_CASES") ? constant("self::CHART_DATA_SORT_CASES") : $sort_order;
                    }
                    $this->getChartData(false, $sort_order, 2);
                    $this->output_handler_->assignValuesToTemplate(array("map_id" => $this->map_id_, "sort_order" => $sort_order, "analysis_title" => $this->language_handler_->getLanguageString('analysis_diagram'), "page_title" => $this->language_handler_->getLanguageString('analysis_diagram_title') . ": ".$this->table_details_->dt_titel));
                    break;
                case constant("self::ANALYSIS_DATATABLE"):
                    $this->getChartData();
                    $this->output_handler_->assignValuesToTemplate(array("analysis_title" => $this->language_handler_->getLanguageString('analysis_table'), "page_title" => $this->language_handler_->getLanguageString('analysis_table_title') . ": ".$this->table_details_->dt_titel));
                    break;
                case constant("self::ANALYSIS_SPATIAL_ASSOCIATION");
                    $arr_dim = $this->table_gateway_->getMoranCasesDimensions($this->table_details_->dt_tabelle, $this->table_details_->e_id);
                    if($arr_dim === false)
                        throw new Exception("Database Error");
                    $map_class_properties = new MapClassProperties($arr_dim['min'], $arr_dim['max'], $arr_dim['cases'], $arr_dim['stddev'], $arr_dim['mean'], $this->map_list_->get('class_width'), $this->map_list_->get('cases_datatype'));
                    $analysis->calculateMoran($map_class_properties, $this->table_gateway_->getMoranData($this->table_details_->dt_tabelle, $this->table_details_->e_id), new FormatHandler(), $is_int, $this->table_details_->e_id);
                    $this->output_handler_->assignValuesToTemplate(array("analysis_title" => $this->language_handler_->getLanguageString('analysis_spatial'), "page_title" => $this->language_handler_->getLanguageString('analysis_spatial_title') . ": ".$this->table_details_->dt_titel));
                    break;
                default:
                    $this->output_handler_->assignValuesToTemplate(array("page_title" => $this->language_handler_->getLanguageString('analysis_stats_title') . ": " . $this->table_details_->dt_titel));
                    $analysis->calculateStats($this->map_class_properties_);
            }
            $this->output_handler_->showTemplate("analysis");
            return true;
        }
        return false;
    }
    
    public function getMap()
    {
        return $this->map_;
    }
    
    public function getTableDetails()
    {
        return $this->table_details_;
    }
    
    private function isLayerModification()
    {
        if($this->request_->issetParameter('get', 'overlay'))
        {
            switch($this->table_details_->e_unit)
            {
                case 'dd':
                    $language_unitstring = $this->language_handler_->getLanguageString('map_unit_dd');
                    break;
                case 'feet':
                    $language_unitstring = $this->language_handler_->getLanguageString('map_unit_feet');
                    break;
                case 'inches':
                    $language_unitstring = $this->language_handler_->getLanguageString('map_unit_inches');
                    break;
                case 'kilometers':
                    $language_unitstring = $this->language_handler_->getLanguageString('map_unit_kilometers');
                    break;
                case 'miles':
                    $language_unitstring = $this->language_handler_->getLanguageString('map_unit_miles');
                    break;
                case 'nauticalmiles':
                    $language_unitstring = $this->language_handler_->getLanguageString('map_unit_nauticalmiles');
                    break;
                default:
                    $language_unitstring = $this->language_handler_->getLanguageString('map_unit_meters');
            }
            $this->output_handler_->assignValuesToTemplate(array("map_title" => $this->table_details_->dt_titel, "m_id" => $this->map_id_, "overlay" => $this->request_->getParameter('get', 'overlay'), "mapunit" => $language_unitstring));
            $layers = new Layers(&$this->map_, &$this->map_list_, $this->request_, false, $this->table_details_->e_id, $this->table_details_->e_projection);
            if($this->request_->issetParameter('post', 'ajax'))
            {
                $arr_return['message'] = $this->language_handler_->getLanguageString('success_message_layer_props');
                $this->output_handler_->showJSON($arr_return);
            }
            else
            {
                $layers->assignPropertiesToTemplate(&$this->output_handler_, $this->request_->getParameter('get', 'overlay'));
                $this->output_handler_->addHeader("Content-Type", "text/html; charset=utf-8");
                $this->output_handler_->showTemplate("layerproperties");
            }
            $arr_params = $this->map_list_->getArray(); 
            $this->session_->set($this->session_name_.'.arr_params', $arr_params);
            return true;
        }
        return false;
    }
}

?>
